# ***************************************************************************
# Copyright IBM Corporation 2021
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#       http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# ***************************************************************************

import os
from bs4 import BeautifulSoup
from importlib import resources
import shutil
from tkltest.util import constants


'''
    CoverageStatisticsHtmlWriter:
    a class to print a CoverageStatistics tree to html directory.
    building the html files:
     each CoverageStatistics has an html file
     each CoverageStatistics is a also line in the table of it parent.
    each CoverageStatistics.DiffCounter is an entry in the table
'''

class CoverageStatisticsHtmlWriter:

    counter_types = {'INSTRUCTION': {'name': 'Instructions', 'is_bar': False},
                     'BRANCH': {'name': 'Branches', 'is_bar': False},
                     'COMPLEXITY': {'name': 'Cxty', 'is_bar': False},
                     'LINE': {'name': 'Lines', 'is_bar': True},
                     'METHOD': {'name': 'Methods', 'is_bar': False},
                     'CLASS': {'name': 'Classes', 'is_bar': False}
                     }

    @staticmethod
    def create_coverage_html_dir(app_statistics, html1_dir, html2_dir, html_combined_dir, html_compare_dir):

        if os.path.isdir(html_compare_dir):
            shutil.rmtree(html_compare_dir)
        os.mkdir(html_compare_dir)
        shutil.copytree(html1_dir + os.sep + 'jacoco-resources', html_compare_dir + os.sep + 'jacoco-resources')
        # shutil.copyfile(constants.TKLTEST_LIB_DIR + os.sep + 'bluebar.gif',
        #                 html_compare_dir + os.sep + 'jacoco-resources' + os.sep + 'bluebar.gif')
        with resources.path('tkltest-lib', 'bluebar.gif') as iconfile:
            shutil.copyfile(iconfile, html_compare_dir + os.sep + 'jacoco-resources' + os.sep + 'bluebar.gif')
        # shutil.copyfile(constants.TKLTEST_LIB_DIR + os.sep + 'goldbar.gif',
        #                 html_compare_dir + os.sep + 'jacoco-resources' + os.sep + 'goldbar.gif')
        with resources.path('tkltest-lib', 'goldbar.gif') as iconfile:
            shutil.copyfile(iconfile, html_compare_dir + os.sep + 'jacoco-resources' + os.sep + 'goldbar.gif')

        for package_statistic in app_statistics.children:
            os.mkdir(html_compare_dir + os.sep + package_statistic.get_pretty_name())
            for class_statistic in package_statistic.children:
                CoverageStatisticsHtmlWriter.__create_coverage_html_file(
                    class_statistic,
                    html_compare_dir + os.sep + package_statistic.get_pretty_name(),
                    html_combined_dir + os.sep + package_statistic.get_pretty_name(),
                    html1_dir + os.sep + package_statistic.get_pretty_name(),
                    html2_dir + os.sep + package_statistic.get_pretty_name())
            CoverageStatisticsHtmlWriter.__create_coverage_html_file(package_statistic, html_compare_dir, html_combined_dir, html1_dir, html2_dir)
        CoverageStatisticsHtmlWriter.__create_coverage_html_file(app_statistics, html_compare_dir, html_combined_dir, html1_dir, html2_dir)


    @staticmethod
    def __create_coverage_html_file(coverage_statistics, html_compare_dir, html_combined_dir, html1_dir, html2_dir):
        '''
        Convert the CoverageStatistics to an html file
        Args:
            coverage_statistics: to convert
            html_compare_dir: the output directory
            html1_dir: the directory of the first suite
            html2_dir: the directory of the second suite
        '''

        if not len(coverage_statistics.counters):
            return
        html_file_name = CoverageStatisticsHtmlWriter.__get_html_file_name(coverage_statistics)

        '''
        reading the html files, that was generated by the jacoco cli.
        from these html files we :
        1. take the html original tables
        2. take the html links to other html files
        3. take the html head
        4. see if we need to correct the reference to the jacoco resources in the html table
        '''

        with open(html_combined_dir + os.sep + html_file_name) as htmls_file:
            soup_combined = BeautifulSoup(htmls_file.read(), 'html.parser').html
        with open(html1_dir + os.sep + html_file_name) as html1_file:
            soup1 = BeautifulSoup(html1_file.read(), 'html.parser').html
        with open(html2_dir + os.sep + html_file_name) as html2_file:
            soup2 = BeautifulSoup(html2_file.read(), 'html.parser').html

        html_head = str(soup1.head)
        # taking the html links:
        html_tree_links = str(soup1.body.div).replace(str(soup1.body.div.find_all(class_='info')[0]), '')
        html_tree_links = html_tree_links.replace('JaCoCo Coverage Report', 'Tackle Coverage Compare Report')

        html_title = '<h1>' + coverage_statistics.get_type() + ': ' + coverage_statistics.get_pretty_name() + '</h1>'
        html_title += '<h1><span style="background-color:gold"> ' + coverage_statistics.test_suite_name1 + \
                      '</span><span> Versus </span>' \
                      '<span style="background-color:cornflowerblue">' + coverage_statistics.test_suite_name2 + '</span></h1>'

        html_table_titles = '<td class="sortable" id="a" onclick="toggleSort(this)">Element</td>'
        for coverage_type in CoverageStatisticsHtmlWriter.counter_types.values():
            if coverage_type['is_bar']:
                html_table_titles += '<td class="down sortable bar" id="b" onclick="toggleSort(this)">Missed ' + \
                                     coverage_type['name'] + '</td>'
                html_table_titles += '<td class="sortable ctr2" onclick="toggleSort(this)">Cov.</td>'
            else:
                html_table_titles += '<td class="sortable ctr1" onclick="toggleSort(this)">Missed</td>'
                html_table_titles += '<td class="sortable ctr2" onclick="toggleSort(this)">' + coverage_type[
                    'name'] + '</td>'

        html_table_head = '<thead><tr>' + html_table_titles + '</tr></thead>'

        html_table_foot = '<tfoot><tr><td>Total</td>' + CoverageStatisticsHtmlWriter.__get_html_table_line(coverage_statistics, summarize=True) + '</tr></tfoot>'

        html_table_body = '<tbody>'
        for child_statistics in coverage_statistics.children:
            if not len(child_statistics.counters):
                continue
            child_name = child_statistics.get_pretty_name()
            child_html_file = CoverageStatisticsHtmlWriter.__get_html_file_name(child_statistics)
            html_table_line = '<tr>'
            href = ''
            if child_html_file:
                href = ' href="' + child_html_file
            html_table_line += '<td><a' + href + '" class="' + CoverageStatisticsHtmlWriter.__get_html_el(child_statistics) + '">' + child_name + '</a></td>'
            html_table_line += CoverageStatisticsHtmlWriter.__get_html_table_line(child_statistics)
            html_table_line += '</tr>'
            html_table_body += html_table_line
        html_table_body += '</tbody>'

        table_legend = '<table><tr>' \
                       '<td class=bar>' + DiffCounterHtmlWriter.get_html_bar_segment(1, 'red', 10) + '</td>' \
                       '<td> Missed by both test suites</td></tr>' \
                       '<tr><td class=bar>' + DiffCounterHtmlWriter.get_html_bar_segment(1, 'gold', 10) + '</td>' \
                       '<td> Missed by first test suite only</td></tr>' \
                       '<tr><td class=bar>' + DiffCounterHtmlWriter.get_html_bar_segment(1, 'blue', 10) + '</td>' \
                       '<td> Missed by second test suite only</td></tr>' \
                       '<tr><td class=bar>' + DiffCounterHtmlWriter.get_html_bar_segment(1, 'green', 10) + '</td>' \
                       '<td> Covered by both test suites</td></tr></table>'

        html_table = '<table class="coverage" cellspacing="0" id="coveragetable">'
        html_table += html_table_head
        html_table += html_table_foot
        html_table += html_table_body
        html_table += '</table>'
        html_table += table_legend

        if '../jacoco-resources' in html_head:
            html_table = html_table.replace('jacoco-resources', '../jacoco-resources')

        html_test_combined_text = '<h1>-----------------------------------------------------------------------------------------------------------</h1>'
        html_test_combined_text += '<h1><span style="background-color:gold"> ' + coverage_statistics.test_suite_name1 + \
                      '</span><span> and </span>' \
                      '<span style="background-color:cornflowerblue">' + coverage_statistics.test_suite_name2 +\
                      '</span><span> Combined </span></h1>'
        html_test_combined_text += str(soup_combined.body.table)
        html_test1_text = '<h1>-----------------------------------------------------------------------------------------------------------</h1>'
        html_test1_text += '<h1><span style="background-color:gold"> ' + coverage_statistics.test_suite_name1 + '</span><span> Coverage Report</span></h1>'
        html_test1_text += str(soup1.body.table)
        html_test2_text = '<h1>-----------------------------------------------------------------------------------------------------------</h1>'
        html_test2_text += '<h1><span style="background-color:cornflowerblue">' + coverage_statistics.test_suite_name2 + '</span><span> Coverage Report</span></h1>'
        html_test2_text += str(soup2.body.table)

        html_text = '<html>' + html_head
        html_text += '<body>'
        html_text += html_tree_links
        html_text += html_title
        html_text += html_table
        html_text += html_test_combined_text
        html_text += html_test1_text
        html_text += html_test2_text
        html_text += '</body></html>'

        with open(html_compare_dir + os.sep + html_file_name, mode='w') as new_html_file:
            new_html_file.write(html_text)


    @staticmethod
    def __get_html_table_line(coverage_statistics, summarize=False):
        '''
        Convert the CoverageStatistics to an html row in the html table
        Args:
            coverage_statistics: - to convert
            summarize: true if it is the last row in the table (in this case we do not create a bar)

        Returns:
            html line - a row in the table
        '''
        line = ''
        for coverage_type, coverage_type_data in CoverageStatisticsHtmlWriter.counter_types.items():
            if coverage_statistics.counters.get(coverage_type) and coverage_statistics.counters[coverage_type].total != 0:
                counter = coverage_statistics.counters[coverage_type]
                if coverage_type_data['is_bar']:
                    if not summarize:
                        # we normalize the bars with the largest bar
                        max_val = max([child.counters[coverage_type].total for child in coverage_statistics.parent.children
                                       if child.counters.get(coverage_type)])
                        line += DiffCounterHtmlWriter.get_html_bar(counter, max_val)
                    else:
                        line += DiffCounterHtmlWriter.get_html_coverage_diff(counter)
                else:
                    line += DiffCounterHtmlWriter.get_html_misses_diff(counter)
            else:
                line += DiffCounterHtmlWriter.get_html_na()
        return line

    @staticmethod
    def __get_html_file_name(coverage_statistics):
        if coverage_statistics.get_type() == 'Class':
            return coverage_statistics.get_pretty_name() + '.html'
        elif coverage_statistics.get_type() == 'Package':
            return coverage_statistics.get_pretty_name() + os.sep + 'index.html'
        elif coverage_statistics.get_type() == 'App':
            return 'index.html'

    @staticmethod
    def __get_html_el(coverage_statistics):
        if coverage_statistics.get_type() == 'Method':
            return 'el_method'
        elif coverage_statistics.get_type() == 'Class':
            return 'el_class'
        elif coverage_statistics.get_type() == 'Package':
           return 'el_package'


class DiffCounterHtmlWriter:
    '''
     methods that translate a DiffCounter class to html code
    the DiffCounter represent a cell in the html table
    '''

    Total_diff = 0;
    @staticmethod
    def get_html_bar_segment(bar_scale, color, val):
        return '<img src = "jacoco-resources/' + color + 'bar.gif" width = "{}" height = "10" title = "{}" alt = "{}" />'.format(
            int(val / bar_scale), val, val)

    @staticmethod
    def __get_html_coverage_compare(counter):
        return '<td class="clr1">{}% vs {}%</td>'\
            .format(int(100 * counter.total_covered1 / counter.total),
                    int(100 * counter.total_covered2 / counter.total))


    @staticmethod
    def get_html_bar(counter, max_val):
        bar_scale = max_val / 100
        ibar = DiffCounterHtmlWriter.get_html_bar_segment(bar_scale, 'red', counter.missed_both)
        ibar += DiffCounterHtmlWriter.get_html_bar_segment(bar_scale, 'gold', counter.missed_only1)
        ibar += DiffCounterHtmlWriter.get_html_bar_segment(bar_scale, 'blue', counter.missed_only2)
        ibar += DiffCounterHtmlWriter.get_html_bar_segment(bar_scale, 'green', counter.missed_none)
        ibar += DiffCounterHtmlWriter.__get_html_coverage_compare(counter)
        return '<td class="bar">' + ibar + '</td>'

    @staticmethod
    def get_html_coverage_diff(counter):
        return '<td class="clr2"> both={} 1st={} 2nd={} none={} total={}</td>' \
                   .format(counter.missed_both, counter.missed_only1, counter.missed_only2, counter.missed_none, counter.total) + \
               DiffCounterHtmlWriter.__get_html_coverage_compare(counter)

    @staticmethod
    def get_html_misses_diff(counter):
        return '<td class="clr2"> {} vs {} </td>'.format(counter.total_missed1, counter.total_missed2) + \
               '<td class="clr1"> {} </td>'.format(counter.total)

    @staticmethod
    def get_html_na():
        return '<td class="bar"></td>' \
               '<td class="clr1"> n/a </td>'
